/**
 * 
 */
package mdp.solve;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.TreeMap;

import java.util.Set;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

import mdp.id.AADDHandler;
import mdp.id.ADDHandler;
import mdp.id.LogicRDDL2DD;
import mdp.id.RDDL2DD;
import mdp.id.RDDL2DD.DEBUG;
import mdp.id.RDDL2DD.ORDER;

import dd.discrete.ADD;
import dd.discrete.ADDDNode;
import dd.discrete.ADDNode;
import dd.discrete.DD;

import util.Pair;
/**
 * @author NR
 *
 * TODO
 * @param <D>
 */
public class DifferenceDisplayer<D extends DD, T extends RDDL2DD<D>> implements Runnable{


	ArrayBlockingQueue<Pair<T, Integer>>	_bq1;
	ArrayBlockingQueue<Pair<T, Integer>> _bq2;
	
	public DifferenceDisplayer(ArrayBlockingQueue<Pair<T, Integer>> pBq1, ArrayBlockingQueue<Pair<T, Integer>> pBq2) {
		_bq1 = pBq1;
		_bq2 = pBq2;
	}
	
//	@Override
	public void run() {
		
		try {
			while( true ) {
				
				Thread.sleep(10000);
				
//				System.out.println(_iter1.isStuffAvailable() + " " + _iter2.isStuffAvailable() );
//				System.out.println(_bq1.size() + " " + _bq2.size() );
				
				if( _bq1.size() == 1 && _bq2.size() == 1 ) {
					Pair<T, Integer> ret1 = _bq1.peek();
					Pair<T, Integer> ret2 = _bq2.peek();
					
					System.out.println("displaying");
					
					showDifference(ret1._o1, ret1._o2 ,ret2._o1, ret2._o2);
//					System.in.read();
					_bq1.remove();
					_bq2.remove();
				}
				
					
				
			}
//				System.out.println(_bq.size());
				
//				System.out.println("*" + _bq1.size());
//				System.out.println("*" + _bq2.size());

		}catch( Exception e ) {
			e.printStackTrace();
		}
		
	}
	
	//does not assume both context have same order
	private void showDifference(T context1, int dd1, T context2, int dd2) {
		
		System.out.println("diff " + context1 + " " + context2);
		Pair<D, TreeMap<Integer, String>> pair = context1.getNewContext();
		D context = pair._o1;
		//transfer diagram to new context
		//align in the process
//		System.out.println("One");
//		System.out.println(context1._context.printNode(dd1));
//		
//		System.out.println("Two");
//		System.out.println(context2._context.printNode(dd2));
		
		int dd1t = transfer(context, context1, dd1, context1._context.getGIDs(dd1),0, pair._o2, 
				context.getConstantNode(context1.getNegInf()));
		System.out.println("transffered one " + context1);
		int dd2t = transfer(context, context2, dd2, context2._context.getGIDs(dd2), 0, pair._o2,
				context.getConstantNode(context2.getNegInf()));
		System.out.println("transffered two " + context2);
		
		//sanity
//		HashMap eval = new HashMap();
//		eval.put("elevator_at_floor__e0_f0", true);
//		eval.put("elevator_at_floor__e0_f1", false);
//		eval.put("elevator_at_floor__e0_f2", false);
//		eval.put("elevator_closed__e0", true);
//		eval.put("elevator_dir_up__e0", true);	
//		eval.put("person_in_elevator_going_down__e0", false);
//		eval.put("person_in_elevator_going_up__e0", false);
//		eval.put("person_waiting_down__f2", false);
//		eval.put("person_waiting_down__f0", false);
//		eval.put("person_waiting_down__f1", false);
//		eval.put("person_waiting_up__f2", false);
//		eval.put("person_waiting_up__f1", false);
//		eval.put("person_waiting_up__f0", false);
//		
//		double val1 = context1.evaluate(dd1, eval);
//		double val2 = context2.evaluate(dd2, eval);
//		double val1t = context.evaluate(dd1t, eval);
//		double val2t = context.evaluate(dd2t, eval);
//		
//		if( val1t != val2t ) {
//			System.err.println("faulty transfer");
//			System.exit(1);
//		}
		//show dds
//		context.getGraph(dd1t).launchViewer();
//		context.getGraph(dd2t).launchViewer();
		
		int diff = context.applyInt(dd1t, dd2t, DD.ARITH_MINUS);
//		System.out.println(context.printNode(diff));
		HashSet nodes = new HashSet();
		((ADD)context).collectLeaves(diff, nodes);
		System.out.println(nodes);
//		System.out.println(context.getMaxValue(id))
//		System.out.println(context)
//		System.out.println(" average : " + sum );
//		System.out.println(" Max : " + Collections.(leaves))
		
//		context.getGraph(dd1t).launchViewer();
//		context.getGraph(dd2t).launchViewer();
//		
//		context.getGraph(diff).launchViewer();
	}
	
	private int transfer(D context, T contexti, int ddi, Set<Integer> gidsi, int top, TreeMap<Integer, String> id2var, int DD_NEG_INF) {
		
		
		
		int newtop = top;
		
		while( newtop != context._alOrder.size()  ){
			int newContextID = (Integer) context._alOrder.get(newtop);
			String newVarName = id2var.get(newContextID);
			int tempHisID = contexti.toContextID(newVarName);
			if( gidsi.contains(tempHisID) ) {
				break;
			}
			++newtop;
		}
		
		top = newtop;
		
		if( top == context._alOrder.size() ) {
//			System.out.println(contexti._context.printNode(ddi));
			double leafval = contexti._context.evaluate(ddi, new ArrayList());
			if( leafval == Double.NaN || leafval == contexti.getNegInf()) {
				System.err.println("NaN produced");
				contexti.showGraph(ddi);
				try
				{
					System.in.read();
				} catch (IOException e)
				{
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
//			System.out.println("Leaf : " + leafval + " " + (leafval == Double.NaN));
			return context.getConstantNode(leafval);
		}
		
		int contextID = (Integer)context._alOrder.get(top);
		String varname = id2var.get(contextID);
		int hisID = contexti._tmVarName2ID.get(varname);
		
		Map<Integer, Boolean> tempAss = new HashMap<Integer, Boolean>(); 
		
		tempAss.put(hisID, true);
		int trueDDi = contexti.simplify(ddi, tempAss);
		
		int contextTruth = transfer(context, contexti, trueDDi, gidsi, top+1, id2var, DD_NEG_INF);
		
		if( contextTruth != DD_NEG_INF ) {
			contextTruth = context.applyInt(
					context.getVarNode(contextID, 0.0d, 1.0d),
					contextTruth,
					DD.ARITH_PROD);	
		}else {
			System.err.println("Saw neg inf... FYI");
			contextTruth = context.getVarNode(contextID, 0.0d, contexti.getNegInf());
		}
		
		tempAss.put(hisID, false);
		int falseDDi = contexti.simplify(ddi, tempAss);
		
		int contextFalse = transfer(context, contexti, falseDDi, gidsi, top+1, id2var, DD_NEG_INF);
		
		if( contextFalse != DD_NEG_INF ) {
			contextFalse = context.applyInt(
					context.getVarNode(contextID, 1.0d, 0.0d),
					contextFalse,
					DD.ARITH_PROD);	
		}else {
			System.err.println("Saw neg inf... FYI");
			contextFalse = context.getVarNode(contextID, contexti.getNegInf(), 0.0d);
		}
		
		return context.applyInt(contextTruth, contextFalse, DD.ARITH_SUM);
	}
	
	public static void main(String[] args) throws Exception {
		
		ArrayBlockingQueue<Pair<ADDHandler, Integer>> bq1 = new ArrayBlockingQueue<Pair<ADDHandler, Integer>>(1);
		
		ArrayBlockingQueue<Pair<ADDHandler, Integer>> bq2 = new ArrayBlockingQueue<Pair<ADDHandler, Integer>>(1);
		
		ADDHandler handler1 = new ADDHandler(DEBUG.PROBLEM_INFO, ORDER.INTERLEAVE);
		ADDHandler handler2 = new ADDHandler(DEBUG.PROBLEM_INFO, ORDER.INTERLEAVE);
		
		System.out.println(handler1);
		System.out.println(handler2);
		FAR<ADDHandler> vic = new FAR<ADDHandler>(
				handler1, args[0], args[1], Double.parseDouble(args[2]), bq1);

//		ValueIteration<ADDHandler> vi = new ValueIteration<ADDHandler>(
//				handler2, args[0], args[1], Double.parseDouble(args[2]), bq2);
		
//		MaxDecomposition<ADDHandler> maxd = new MaxDecomposition<ADDHandler>(handler2, 
//				args[0], args[1], Double.parseDouble(args[2]), bq2);
		
//		ValueIterationCutsetConditioning<ADDHandler> rc= new ValueIterationCutsetConditioning<ADDHandler>
//			(handler2, args[0], args[1], 0.1, Double.parseDouble(args[4]), bq2);
		
//		PolicyIterationConcurrent<ADDHandler> spi = new PolicyIterationConcurrent<ADDHandler>(handler2, args[0], args[1],  
//				bq2, Integer.parseInt(args[3]));
//		
		
//		AADDHandler handler2 = new AADDHandler(LogicRDDL2DD.DEBUG.PROBLEM_INFO, 
//				LogicRDDL2DD.ORDER.INTERLEAVE);
//		ValueIterationAADD<AADDHandler> viaadd = new ValueIterationAADD<AADDHandler>(handler2, args[0], 
//				args[1], Double.parseDouble(args[2]), null);
		
//		ValueIterationConcurrentAADD<AADDHandler> vicaadd = new ValueIterationConcurrentAADD<AADDHandler>(handler2, 
//				args[0], args[1], Double.parseDouble(args[2]), null);
		
		Thread vicWorker = new Thread(vic, "Concurrent VI");
			
//		Thread maxWorker = new Thread(maxd, "max decomposition");

//		Thread viWorker = new Thread(vi, "VI");
//		
//		Thread rcWorker = new Thread(rc, "RC");
//		
//		Thread spiWorker = new Thread(spi, "SPI");
		
//		Thread viaaddWorker = new Thread(viaadd, "VIAADD");
		
//		Thread vicaaddWorker = new Thread(vicaadd, "VIAADD");
		
		
		DifferenceDisplayer<ADD, ADDHandler> differ = new DifferenceDisplayer<ADD, ADDHandler>(bq1,bq2);
		Thread differWorker = new Thread(differ, "differ");
		
//		viWorker.start();
//		maxWorker.start();
//		rcWorker.start();
//		spiWorker.start();
		vicWorker.start();
		
		differWorker.start();
//		vicaaddWorker.start();
//		vicaaddWorker.join();
//		System.out.println("out");
//		int that = handler2.getADD(handler2.getValueDD(), handler1);
//		System.out.println("putting");
//		bq2.put(new Pair<ADDHandler, Integer>(handler1, that));
		
//		maxWorker.start();
		
		
	}
}
